emacs org快速选择代码块

这是我org笔记中用来快速选择,不同代码块的一个功能。我模仿了vim那样按下 Ctrl+c S 键就进入一个选择模式,然后按下 j k 可以用来选择上下文不同的代码块,按 y或者enter 选择复制文本,按 q 退出,就好像这是一个vim功能的模式一样。

原理就是建立一个org-quick-select-temp-keymap的map,然后把原本的map用org-quick-select-original-keymap保存。然后通过org-quick-select-disable-other-keys,把buffer设置成只读模式,并且映射一些临时的键就起到了构建一个类似vim mode那样的模式了。

(defvar org-quick-select-temp-keymap nil)
(defvar org-quick-select-restrict-mode nil)  ; 用来标记是否启用了限制模式
(defvar org-quick-select-original-keymap nil)  ; 保存原始的 keymap

(defun org-quick-select-toggle-restrict-keys ()
  "切换限制按键的状态,按下 C-c S 时启用或关闭限制模式,仅在 org-mode 中生效。"
  (interactive)
  (if (not (derived-mode-p 'org-mode))
      (message "此功能仅在 org-mode 中可用。")
    (if org-quick-select-restrict-mode
        (org-quick-select-enable-all-keys)  ; 如果限制模式已启用,按下时解除限制
      (org-quick-select-disable-other-keys))))  ; 否则启用限制模式

(defun org-quick-select-disable-other-keys ()
  "屏蔽所有其他按键,仅允许 j/k 控制光标上下移动,并禁用输入,仅在 org-mode 中生效。"
  (interactive)
  (if (not (derived-mode-p 'org-mode))
      (message "此功能仅在 org-mode 中可用。")
    ;; 保存当前的 keymap 到 org-quick-select-original-keymap
    (setq org-quick-select-original-keymap (current-local-map))
    ;; 创建一个临时的keymap
    (setq org-quick-select-temp-keymap (make-sparse-keymap))
    ;; 只允许 'j', 'k' 控制上下移动
    (define-key org-quick-select-temp-keymap (kbd "j") 'org-select-src-block-content-next)
    (define-key org-quick-select-temp-keymap (kbd "k") 'org-select-src-block-content-prev)
    ;; 将 M-w 映射为复制当前选择内容
    (define-key org-quick-select-temp-keymap (kbd "M-w") 'org-quick-select-copy-selection)
    ;; 将 y 映射为复制当前选择内容
    (define-key org-quick-select-temp-keymap (kbd "y") 'org-quick-select-copy-selection)
    ;; 将 Enter 键映射为复制当前选择内容
    (define-key org-quick-select-temp-keymap (kbd "RET") 'org-quick-select-copy-selection)

    (define-key org-quick-select-temp-keymap (kbd "q") 'org-quick-select-toggle-restrict-keys)

    ;; 禁用其它键,防止意外的按键操作
    (use-local-map org-quick-select-temp-keymap)
    ;; 启用只读模式,禁用其他输入
    (setq buffer-read-only t)
    (setq org-quick-select-restrict-mode t)  ;; 设置标记为限制模式已启用
    (message "键盘被限制,只有 j/k 可用,按 C-c S 解除限制。")))

(defun org-quick-select-copy-selection ()
  "拷贝当前选择的内容到剪贴板。"
  (interactive)
  (if (use-region-p)
      (progn
        (kill-ring-save (region-beginning) (region-end))  ;; 拷贝选中的内容
        (message "已拷贝选择的内容"))
    (message "没有选择任何内容。")))

(defun org-select-src-block-content-next ()
  "搜索下一个 code block,跳转到位置并全选内容,排除 'begin_src' 和 'end_src'。"
  (interactive)
  (let ((start (point)))
    ;; 搜索下一个 'begin_src',跳到该位置
    (if (re-search-forward "^#\\+begin_src" nil t)
        (progn
          ;; 跳过 'begin_src' 之前的部分
          (forward-line 1)
          ;; 搜索 'end_src',选择从 'begin_src' 到 'end_src' 之间的内容
          (let ((end (save-excursion
                       (re-search-forward "^#\\+end_src" nil t)
                       (point-at-bol)))) ;; 使用 point-at-bol 获取当前行的开始位置
            (push-mark (point) t t)
            (goto-char end)
            (activate-mark)))
      ;; 如果没有找到下一个 code block,则给出提示
      (message "没有找到下一个 code block"))))

(defun org-select-src-block-content-prev ()
  "搜索上一个 code block,跳转到位置并全选内容,排除 'begin_src' 和 'end_src'。"
  (interactive)
  (let ((start (point)))
    ;; 搜索上一个 'end_src'
    (if (re-search-backward "^#\\+end_src" nil t)
        (progn
          ;; 搜索对应的 'begin_src'
          (if (re-search-backward "^#\\+begin_src" nil t)
              (progn
                ;; 跳过 'begin_src' 行
                (forward-line 1)
                (let ((begin (point))
                      (end (save-excursion
                             (re-search-forward "^#\\+end_src" nil t)
                             (point-at-bol))))
                  (push-mark (point) t t)
                  (goto-char end)
                  (activate-mark)))
            (message "没有找到对应的 begin_src"))
          )
      ;; 如果没有找到上一个 code block,则给出提示
      (message "没有找到上一个 code block"))))

(defun org-quick-select-enable-all-keys ()
  "恢复所有按键并解除输入限制,仅在 org-mode 中生效。"
  (interactive)
  (if (not (derived-mode-p 'org-mode))
      (message "此功能仅在 org-mode 中可用。")
    ;; 恢复原始的 keymap
    (use-local-map org-quick-select-original-keymap)
    ;; 解除只读模式,允许输入
    (setq buffer-read-only nil)
    (setq org-quick-select-restrict-mode nil)  ;; 设置标记为限制模式已解除
    (message "已恢复所有按键和输入功能。")))